#include <types.h>
#include <sys_config.h>
#include <retcode.h>
#include <api/libc/string.h>
#include <api/libc/printf.h>
#include <osal/osal.h>
#include <hld/hld_dev.h>
#include <hld/dis/VPO.h>
#include <hld/decv/vdec_driver.h>
#include <hld/decv/decv.h>
#include <api/libfs2/stat.h>
#include <api/libfs2/dirent.h>
#include <api/libfs2/statvfs.h>
#include <api/libfs2/unistd.h>
#include <api/libfs2/stdio.h>

#include <api/libimagedec/imagedec.h>
#include <api/libmp/pe.h>
#include "music/plugin.h"
#include "music/music_engine.h"
#include "image/image_engine.h"
#ifdef MP4_CONTAINER
#include "av_player/av_player.h"
extern F_HANDLE m_mp4_test_file;
#endif

#ifdef SUPPORT_ES_PLAYER
#include <api/libstream/libstream.h>
int g_libstream_enable = 0;
#endif



#define PE_CACHE_NUM    2

//#define LOG_PE_CACHE
//#define ENABLE_PE_FLAG
#define PE_FLAG_DATA_READY  0x00000001

#define PE_PREVIOUS_BUFFER_SIZE 10240
UINT32 pe_pre_buffer_size = 10240;
#ifdef PE_CALLBACK_TO_APP_ENABLE		
pccache_callback_func app_callback;
#endif

#ifdef  DUAL_ENABLE
#define PE_CACHE_MUTEX_CREATE osal_dual_mutex_create
#define PE_CACHE_MUTEX_LOCK osal_dual_mutex_lock
#define PE_CACHE_MUTEX_UNLOCK osal_dual_mutex_unlock
#define PE_CACHE_MUTEX_DELETE osal_dual_mutex_delete
#else
#define PE_CACHE_MUTEX_CREATE osal_mutex_create
#define PE_CACHE_MUTEX_LOCK osal_mutex_lock
#define PE_CACHE_MUTEX_UNLOCK osal_mutex_unlock
#define PE_CACHE_MUTEX_DELETE osal_mutex_delete
#endif

static pe_cache_cb_fp pe_cache_net_fp[PE_NET_FILE_TYPE_MAX];
static volatile ID pe_cache_mutex = OSAL_INVALID_ID;
//static volatile pe_cache *pe_cache_info = NULL; //[PE_CACHE_NUM];
static volatile pe_cache pe_cache_info[PE_CACHE_NUM];
static volatile pe_cache_ex * pe_cache_info_ex = NULL;
static ID pe_task_id = OSAL_INVALID_ID;
static int pe_cache_count = 0;
#ifdef DUAL_ENABLE
static volatile pe_cache_cmd *pe_cache_cmd_buf = NULL;
#endif

UINT32 pe_cache_get_data_len(int cache_id);

#ifdef  DUAL_ENABLE
static void pe_cache_process_cmd(void)
{
    char *filename; 
    UINT8 *cache_buff; 
    UINT32 cache_size;
    UINT32 block_size;
    int cache_id;
    off_t offset;
    UINT32 *offset_p;
    int where;
    int ret;

    PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
    if(pe_cache_mutex != INVALID_ID && pe_cache_cmd_buf && pe_cache_cmd_buf->status == PE_CACHE_CMD_STATUS_NEW)
    {
        switch(pe_cache_cmd_buf->type)
        {
            case PE_CACHE_CMD_OPEN:
                filename = (char *)(pe_cache_cmd_buf->param[0]);
                cache_buff = (UINT8 *)(pe_cache_cmd_buf->param[1]);
                cache_size = pe_cache_cmd_buf->param[2];
                block_size = pe_cache_cmd_buf->param[3];
                PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
                cache_id = pe_cache_open(filename, cache_buff, cache_size, block_size);
                PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
                pe_cache_cmd_buf->param[0] = cache_id;
                pe_cache_cmd_buf->status = PE_CACHE_CMD_STATUS_IMPLEMENTED;
                break;
             case PE_CACHE_CMD_CLOSE:
                cache_id = pe_cache_cmd_buf->param[0];
                PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
                pe_cache_close(cache_id);
                PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
                pe_cache_cmd_buf->status = PE_CACHE_CMD_STATUS_IMPLEMENTED;
                break;
             case PE_CACHE_CMD_SEEK:
                cache_id = pe_cache_cmd_buf->param[0];
                offset_p = (UINT32 *)(&offset);
                *offset_p = pe_cache_cmd_buf->param[1];
                *(offset_p+1) = pe_cache_cmd_buf->param[2];
                where = pe_cache_cmd_buf->param[3];
                PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
                ret = pe_cache_seek(cache_id, offset, where);
                PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
                pe_cache_cmd_buf->param[0] = ret;
                pe_cache_cmd_buf->status = PE_CACHE_CMD_STATUS_IMPLEMENTED;
                break;
        }
    }
    PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
}
#endif

static void pe_cache_task(UINT32 para1, UINT32 para2)
{
    int i;
	pe_cache	pc;
    pe_cache_ex pc_ex;
    size_t bytes_read, bytes_returned;

    while(1)
    {
#ifdef  DUAL_ENABLE
        pe_cache_process_cmd();
#endif
        for(i=0; i<PE_CACHE_NUM; i++)
        {
            PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
            if(PE_CACHE_OPENED == pe_cache_info_ex[i].status)
            {
                PE_CACHE_MUTEX_LOCK(pe_cache_info_ex[i].mutex, OSAL_WAIT_FOREVER_TIME);
		  MEMCPY(&pc, (const void *)&pe_cache_info[i], sizeof(pe_cache));
                MEMCPY(&pc_ex, (const void *)&pe_cache_info_ex[i], sizeof(pe_cache_ex));
                PE_CACHE_MUTEX_UNLOCK(pe_cache_info_ex[i].mutex);
                //if((pc.data_len < pc.cache_size) && (PE_FILE_READING == pc.sub_status))
                //pe_cache have a previous buffer can't be over write
                if((pc_ex.data_len < (pc_ex.cache_size - pe_pre_buffer_size)) \
					&& (PE_FILE_READING == pc_ex.sub_status))
                {
                    if(pc_ex.wr_pos >= pc_ex.rd_pos)
                        bytes_read = pc_ex.cache_size - pc_ex.wr_pos;
                    else
                        //bytes_read = pc.rd_pos - pc.wr_pos;
                        //pe_cache have a previous buffer can't be over write
                        bytes_read = pc_ex.rd_pos - pc_ex.wr_pos - pe_pre_buffer_size;
                    if(bytes_read > pc.block_size)
                        bytes_read = pc.block_size;
                    bytes_returned = pc.file_read(&pc_ex.cache_buff[pc_ex.wr_pos], bytes_read, 1, pc.fp);
#ifdef DUAL_ENABLE                    
                    osal_cache_flush(&pc_ex.cache_buff[pc_ex.wr_pos], bytes_read);
#endif

                    PE_CACHE_MUTEX_LOCK(pe_cache_info_ex[i].mutex, OSAL_WAIT_FOREVER_TIME);
                    if(pc.file_tell)
                    {
                   	       pe_cache_info_ex[i].file_offset = pc.file_tell(pc.fp);
                    }
                    else
                    {
                           pe_cache_info_ex[i].file_offset +=bytes_returned;
                    }
                    if(!bytes_returned)
                    {
                   		if(pc.file_eof!=NULL)
                        		pe_cache_info_ex[i].sub_status = pc.file_eof(pc.fp) ? PE_FILE_EOF : PE_FILE_FAILED;
			   	else 
			   		pe_cache_info_ex[i].sub_status =PE_FILE_EOF;
                    }
                    else
                    {
                        pe_cache_info_ex[i].wr_pos += (UINT32)bytes_returned;
                        if(pe_cache_info_ex[i].wr_pos >= pe_cache_info_ex[i].cache_size)
                            pe_cache_info_ex[i].wr_pos -= pe_cache_info_ex[i].cache_size;
                        pe_cache_info_ex[i].data_len += (UINT32)bytes_returned;
                    }
					
#ifdef ENABLE_PE_FLAG
                    if(pe_cache_info[i].req_size)
                    {
                        pe_cache_info[i].req_size = 0;
                        osal_flag_set(pe_cache_info[i].flag, PE_FLAG_DATA_READY);
                    }
#endif
                    PE_CACHE_MUTEX_UNLOCK(pe_cache_info_ex[i].mutex);
                }
            }
        PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
        osal_task_sleep(1);
        }
    }
}

int pe_cache_init()
{
	int i;
	OSAL_T_CTSK t_ctsk;

    if(pe_task_id != OSAL_INVALID_ID)
        return 0;

    if(pe_cache_info_ex == NULL)
    {
#ifdef  DUAL_ENABLE
        pe_cache_info_ex = (pe_cache_ex *)malloc_sm(sizeof(pe_cache_ex)*PE_CACHE_NUM);
#else
        pe_cache_info_ex = (pe_cache *)MALLOC(sizeof(pe_cache_ex)*PE_CACHE_NUM);
#endif
        if(pe_cache_info_ex == NULL)
        {
            ASSERT(0);
        }
        MEMSET((void *)pe_cache_info_ex, 0, (sizeof(pe_cache_ex)*PE_CACHE_NUM));
        for(i=0; i<PE_CACHE_NUM; i++)
        {
            pe_cache_info_ex[i].mutex = INVALID_ID;
        }
    }
    else
    {
        //ASSERT(0);
    }

    for(i=0; i<PE_CACHE_NUM; i++)
    {
        if (pe_cache_info_ex[i].mutex != INVALID_ID)
        {
            PE_CACHE_MUTEX_DELETE(pe_cache_info_ex[i].mutex);
            pe_cache_info_ex[i].mutex = INVALID_ID;
        }	
    }
	
    MEMSET((void *)pe_cache_info, 0, (sizeof(pe_cache)*PE_CACHE_NUM));
	MEMSET((void *)pe_cache_info_ex, 0, (sizeof(pe_cache_ex)*PE_CACHE_NUM));
    for(i=0; i<PE_CACHE_NUM; i++)
    {
        pe_cache_info_ex[i].status = PE_CACHE_CLOSED;
        pe_cache_info_ex[i].mutex = PE_CACHE_MUTEX_CREATE();
#ifdef ENABLE_PE_FLAG
        pe_cache_info[i].flag = osal_flag_create(0);
#endif
    }
    for(i=0; i<PE_NET_FILE_TYPE_MAX;i++)
    {//init callback for net file
        pe_cache_net_fp[i].file_open = NULL;
    	pe_cache_net_fp[i].file_read = NULL;
	    pe_cache_net_fp[i].file_seek = NULL;
	    pe_cache_net_fp[i].file_eof = NULL;
	    pe_cache_net_fp[i].file_tell = NULL;
	    pe_cache_net_fp[i].file_close = NULL;
    }
    if(pe_cache_mutex == INVALID_ID)
    {
        pe_cache_mutex = PE_CACHE_MUTEX_CREATE();
    }
    else
    {
        //ASSERT(0);
    }
    ASSERT(pe_cache_mutex != INVALID_ID);

#ifdef  DUAL_ENABLE
    if(pe_cache_cmd_buf == NULL)
    {
        pe_cache_cmd_buf = (pe_cache_cmd *)malloc_sm(sizeof(pe_cache_cmd));
        if(pe_cache_cmd_buf == NULL)
        {
            ASSERT(0);
        }

        pe_cache_cmd_buf->status = PE_CACHE_CMD_STATUS_IMPLEMENTED;
        pe_cache_cmd_buf->type = PE_CACHE_CMD_NULL;
    }
    else
    {
        ASSERT(0);
    }
    video_engine_pe_cache_init(pe_cache_info_ex, pe_cache_cmd_buf, pe_cache_mutex);
#endif    

    if(pe_task_id == OSAL_INVALID_ID)
    {
    	t_ctsk.stksz = 0x1800;
    	t_ctsk.quantum	= 4;//10//4
    	t_ctsk.itskpri	= OSAL_PRI_HIGH;//OSAL_PRI_NORMAL//OSAL_PRI_HIGH
    	t_ctsk.name[0]	= 'P';
    	t_ctsk.name[1]	= 'E';
    	t_ctsk.name[2]	= 'C';
    	t_ctsk.task = (FP)pe_cache_task;
    	pe_task_id = osal_task_create(&t_ctsk);
        ASSERT(pe_task_id != OSAL_INVALID_ID);
    }
    else
    {
        ASSERT(0);        
    }

    return 0;
}

int pe_cache_release(void)
{
    int i;

#if 0
	// bug : playing video, plug out usb device, system reset. 
	// solution:
	// don't free pe_cache_info, becasue pe_cache_close will use this structure.
	
    if(pe_cache_info)
    {
        for(i=0; i<PE_CACHE_NUM; i++)
        {
            PE_CACHE_MUTEX_DELETE(pe_cache_info[i].mutex);
        }
#ifdef  DUAL_ENABLE
        free_sm(pe_cache_info, sizeof(pe_cache)*PE_CACHE_NUM);
#else
        FREE(pe_cache_info);
#endif
        pe_cache_info = NULL;

        
    }
    else
    {
        ASSERT(0);
    }
#endif

	if(pe_task_id != OSAL_INVALID_ID)
	{
	    osal_task_delete(pe_task_id);
		pe_task_id = OSAL_INVALID_ID;
	}
	
#ifdef  DUAL_ENABLE
    if(pe_cache_cmd_buf)
    {
	    if(pe_cache_mutex != OSAL_INVALID_ID)
	    {
	    	PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
	    }
        free_sm(pe_cache_cmd_buf, sizeof(pe_cache_cmd));
        pe_cache_cmd_buf = NULL;
		if(pe_cache_mutex != OSAL_INVALID_ID)
		{
			PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
		}
    }
    else
    {
        //ASSERT(0);
    }

	// Roman add at 20100705 to fix bug:
	// jpeg+mp3 burning test crash.
	video_engine_pe_cache_release();
#endif  
	if(pe_cache_mutex != OSAL_INVALID_ID)
	{
    	PE_CACHE_MUTEX_DELETE(pe_cache_mutex);
    	pe_cache_mutex = OSAL_INVALID_ID;
	}

	// avoid plug-out USB system wait when playing Video
    if (pe_cache_info_ex)
    {
        for(i=0; i<PE_CACHE_NUM; i++)
        {
            pe_cache_info_ex[i].sub_status = PE_FILE_FAILED;
        }
    }
    return 0;
}

void pe_cache_register_net_fp(pe_cache_cb_fp net_cb_fp,int type)
{
	if(type >=PE_NET_FILE_TYPE_MAX)
		return;
	pe_cache_net_fp[type].file_open= net_cb_fp.file_open;
	pe_cache_net_fp[type].file_close = net_cb_fp.file_close;
	pe_cache_net_fp[type].file_eof = net_cb_fp.file_eof;
	pe_cache_net_fp[type].file_read = net_cb_fp.file_read;
	pe_cache_net_fp[type].file_seek = net_cb_fp.file_seek;
	pe_cache_net_fp[type].file_tell = net_cb_fp.file_tell;
}

#ifdef PE_CALLBACK_TO_APP_ENABLE

UINT8 app_run_callback=0;

void pe_cache_register_callback_func(pccache_callback_func callback)
{
	if(callback != NULL)
		app_callback=callback;
}

#endif
/*
void pe_cache_cleanup()
{
	int i;
	
	for(i=0; i<PE_CACHE_NUM; i++)
	{
		pe_cache_info[i].status = PE_CACHE_CLOSED;
		osal_mutex_delete(pe_cache_info[i].mutex);
#ifdef ENABLE_PE_FLAG
		osal_flag_delete(pe_cache_info[i].flag);
#endif
	}
	
	osal_mutex_delete(pe_cache_mutex);
	pe_cache_mutex = INVALID_ID;

	osal_task_delete(pe_task_id);
	pe_task_id = OSAL_INVALID_ID;
}
*/

int pe_cache_open(char *filename, UINT8 *cache_buff, UINT32 cache_size, UINT32 block_size)
{
    int i;
    int cache_id = -1;

    PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
    for(i=0; i<PE_CACHE_NUM; i++)
    {
        if(PE_CACHE_CLOSED == pe_cache_info_ex[i].status)
        {
#ifdef SUPPORT_ES_PLAYER
			g_libstream_enable = 0;
#endif
			if(NULL == cache_buff)
			{
				cache_buff = (UINT8 *)MALLOC(cache_size);
				pe_cache_info[i].internal_cache = 1;
			}
			else
				pe_cache_info[i].internal_cache = 0;
			//need a function use filename to separate net file from hdd file
			if(!MEMCMP(filename,"http://",7))
			{//Net file
			//===========
#ifdef _INVW_JUICE
				pe_pre_buffer_size = 10240;
#else			
				pe_pre_buffer_size = 2*1024*1024;
#endif			

#ifdef _INVW_JUICE
				pe_cache_info[i].fp = pe_cache_net_fp[PE_NET_FILE_TYPE_URL].file_open(filename,cache_buff,0x120000,0);//filename, buf, cache length, offset
#else
				pe_cache_info[i].fp = pe_cache_net_fp[i].file_open(filename,cache_buff,0x120000,0);//filename, buf, cache length, offset
#endif				
				if(NULL == pe_cache_info[i].fp)
				{
					if(pe_cache_info[i].internal_cache)
						FREE(cache_buff);
					break;
				}

#ifdef _INVW_JUICE
				pe_cache_info[i].file_read = pe_cache_net_fp[PE_NET_FILE_TYPE_URL].file_read;//url_read;
#else
				pe_cache_info[i].file_read = pe_cache_net_fp[i].file_read;//url_read;
#endif				
				pe_cache_info[i].file_seek = NULL;
				pe_cache_info[i].file_eof = NULL;
				pe_cache_info[i].file_tell = NULL;
#ifdef _INVW_JUICE
				pe_cache_info[i].file_close = pe_cache_net_fp[PE_NET_FILE_TYPE_URL].file_close;//url_close
#else
				pe_cache_info[i].file_close = pe_cache_net_fp[i].file_close;//url_close
#endif				
				//===========
				pe_cache_info[i].file_size = 0;
			}
#ifdef SUPPORT_ES_PLAYER
			else if(is_ali_stream(filename) == 1)
			{
				//libc_printf("%s : cache size %d block size %d\n", __FUNCTION__, cache_size, block_size);
				g_libstream_enable = 1;
				pe_pre_buffer_size = cache_size;//10240;			
				pe_cache_info[i].fp = pe_cache_net_fp[PE_ALI_SPECIAL_STREAM].file_open(filename
					, NULL, 0, 0);
				if(NULL == pe_cache_info[i].fp)
				{
					if(pe_cache_info[i].internal_cache)
						FREE(cache_buff);
					break;
				}
				
				pe_cache_info[i].file_read = pe_cache_net_fp[PE_ALI_SPECIAL_STREAM].file_read;
				pe_cache_info[i].file_seek = pe_cache_net_fp[PE_ALI_SPECIAL_STREAM].file_seek;
				pe_cache_info[i].file_eof = NULL;
				pe_cache_info[i].file_tell = pe_cache_net_fp[PE_ALI_SPECIAL_STREAM].file_tell;
				pe_cache_info[i].file_close = pe_cache_net_fp[PE_ALI_SPECIAL_STREAM].file_close;
				pe_cache_info[i].file_size = (long long)LS_END_OFFSET;				
			}			
#endif		
			else
			{//HDD file
				pe_pre_buffer_size = 10240;
				pe_cache_info[i].fp = fopen(filename, "rb");
				if(NULL == pe_cache_info[i].fp)
				{
					if(pe_cache_info[i].internal_cache)
					FREE(cache_buff);
					break;
				}
				pe_cache_info[i].file_read = fread;
				pe_cache_info[i].file_seek = fseek;
				pe_cache_info[i].file_eof = feof;
				pe_cache_info[i].file_tell = ftell;
				pe_cache_info[i].file_close = fclose;
				fseek(pe_cache_info[i].fp, 0, SEEK_END);
				pe_cache_info[i].file_size = ftell(pe_cache_info[i].fp);
				fseek(pe_cache_info[i].fp, 0, SEEK_SET);
			}
		#ifdef MP4_CONTAINER
		m_mp4_test_file = pe_cache_info[i].fp;
		#endif
	        pe_cache_info_ex[i].file_offset =0;//init file offset
	        pe_cache_info_ex[i].cache_buff = cache_buff;
	        pe_cache_info_ex[i].cache_size = cache_size;//(cache_size/block_size)*block_size;
	        pe_cache_info[i].block_size = block_size;
	        pe_cache_info_ex[i].data_len = 0;
	        pe_cache_info_ex[i].rd_pos = 0;
	        pe_cache_info_ex[i].wr_pos = 0;
	        pe_cache_info_ex[i].status = PE_CACHE_OPENED;
	        pe_cache_info_ex[i].sub_status = PE_FILE_READING;
#ifdef PE_CALLBACK_TO_APP_ENABLE					
 		if(app_callback !=NULL)			
     			pe_cache_info[i].callback= app_callback;	
#endif	 
#ifdef ENABLE_PE_FLAG
            pe_cache_info[i].req_size = 0;
#endif
#ifdef LOG_PE_CACHE
            pe_cache_info[i].read_delay = 0;
            pe_cache_info[i].read_count = 0;
#endif
	     pe_cache_count ++;
            cache_id = i;
            break;
        }
    }
    PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);

    return cache_id;
}

//important!!!must set the cache_id to -1 and set cache_buff to NULL, after call pe_cache_close
UINT32 pe_cache_close(int cache_id)
{
    PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
    if(PE_CACHE_OPENED == pe_cache_info_ex[cache_id].status)
    {
        //if(pe_cache_info[cache_id].fp &&pe_cache_info[cache_id].file_close!=NULL)//yuliang_net
           // fclose(pe_cache_info[cache_id].fp);
	if(pe_cache_info[cache_id].fp)
	{
		pe_cache_info[cache_id].file_close(pe_cache_info[cache_id].fp);
		pe_cache_info[cache_id].fp = NULL;
	}
        if(pe_cache_info[cache_id].internal_cache)
            FREE(pe_cache_info_ex[cache_id].cache_buff);
        pe_cache_info_ex[cache_id].status = PE_CACHE_CLOSED;
		pe_cache_count --;
    }
    PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
    return 0;
}

void pe_cache_reset_info()
{
#ifdef LOG_PE_CACHE
    int i;
    for(i=0; i<PE_CACHE_NUM; i++)
    {
        pe_cache_info[i].read_delay = 0;
        pe_cache_info[i].read_count = 0;
    }
#endif
}

void pe_cache_print_info()
{
#ifdef LOG_PE_CACHE
    int i;
    for(i=0; i<PE_CACHE_NUM; i++)
        libc_printf("cache_id=%d, delay=%d, count=%d\n", i, pe_cache_info[i].read_delay, pe_cache_info[i].read_count);
#endif
}

UINT32 pe_cache_read(int cache_id, UINT8 *buff, UINT32 size, UINT32 threshold)
{
    UINT32 bytes_returned = 0;
    pe_cache_ex *pc_ex = &pe_cache_info_ex[cache_id];
	UINT8 get_data_times=0;
    ASSERT((UINT32)cache_id < PE_CACHE_NUM);

    if(PE_CACHE_AUTO_THRESHOLD == threshold)
		threshold = (pe_cache_count > 1) ? (pc_ex->cache_size - pe_pre_buffer_size) : 0;

#ifdef LOG_PE_CACHE
    UINT32 delta = osal_get_tick() - pc->read_tick;
    if(delta)
        libc_printf("%s[%d]: size=0x%x, threshold=0x%x, data_len=0x%x, speed=%d KB/s\n", __FUNCTION__, cache_id, 
                    size, threshold, pe_cache_get_data_len(cache_id), pc->read_size/delta);
    pc->read_size = size;
    UINT32 ts = osal_get_tick();
#endif

    if(size > threshold)
        threshold = size;
#ifdef ENABLE_PE_FLAG
    if(threshold > pe_cache_get_data_len(cache_id) && !pe_cache_check_eof(cache_id))
    {
        UINT32 flgptn = 0;
        BOOL wait_data = TRUE;
        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
        if(PE_FILE_FAILED == pc_ex->sub_status)
        {
            PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
            return 0;
        }
        pc->req_size = size;
        PE_CACHE_MUTEX_UNLOCK(pc->mutex);
        osal_flag_wait(&flgptn, pc->flag, PE_FLAG_DATA_READY, OSAL_TWF_ANDW|OSAL_TWF_CLR, OSAL_WAIT_FOREVER_TIME);
    }
#else
    while((threshold > pe_cache_get_data_len(cache_id)) && !pe_cache_check_eof(cache_id))
    {
#ifdef PE_CALLBACK_TO_APP_ENABLE		
    	
    		if((pc->callback != NULL)&&(get_data_times > 100))
    		{
    				get_data_times=0;
				app_run_callback=1;	
				pc->callback(1,1);
    		}
        	osal_task_sleep(1);
		if(pc->callback != NULL)
			get_data_times++;
#else
        osal_task_sleep(1);
#endif
    }
#endif

#ifdef PE_CALLBACK_TO_APP_ENABLE	
	if(app_run_callback==1)
	{
			pc->callback(0,0);
			app_run_callback=0;
	}
#endif
#ifdef LOG_PE_CACHE
    pc->read_delay += osal_get_tick() - ts;
    pc->read_count++;
#endif

    if(PE_CACHE_OPENED == pc_ex->status)
    {
        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
        do
        {
            if(PE_FILE_FAILED == pc_ex->sub_status)
                break;

            if(pc_ex->data_len)
            {
                if(pc_ex->rd_pos < pc_ex->wr_pos)
                {
                    bytes_returned = pc_ex->wr_pos - pc_ex->rd_pos;
                    if(bytes_returned > size)
                        bytes_returned = size;
                    MEMCPY(buff, &pc_ex->cache_buff[pc_ex->rd_pos], bytes_returned);
                    pc_ex->rd_pos += bytes_returned;
                }
                else
                {
                    bytes_returned = pc_ex->cache_size - pc_ex->rd_pos;
                    if(bytes_returned > size)
                        bytes_returned = size;
                    MEMCPY(buff, &pc_ex->cache_buff[pc_ex->rd_pos], bytes_returned);
                    pc_ex->rd_pos += bytes_returned;
                    if(pc_ex->rd_pos >= pc_ex->cache_size)
                        pc_ex->rd_pos = 0;

                    size -= bytes_returned;
                    if(size)
                    {
                        if(size > pc_ex->wr_pos)
                            size = pc_ex->wr_pos;
                        MEMCPY(&buff[bytes_returned], pc_ex->cache_buff, size);
                        pc_ex->rd_pos = size;
                        bytes_returned += size;
                    }
                }
                pc_ex->data_len -= bytes_returned;
            }
        }while(0);
        PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
    }

#ifdef LOG_PE_CACHE
    delta = osal_get_tick() - ts;
    if(delta)
        libc_printf("read_speed=%d KB/s, read_time=%d\n", bytes_returned/delta, delta);
    else
        libc_printf("read_speed=[MAX]\n");
    pc->read_tick = osal_get_tick();
#endif

    return bytes_returned;
}

UINT32 pe_cache_get(int cache_id, UINT8 **ptr, UINT32 size, UINT32 threshold)
{
    UINT32 bytes_returned = 0;
    pe_cache_ex *pc_ex = &pe_cache_info_ex[cache_id];

    ASSERT((UINT32)cache_id < PE_CACHE_NUM);

    if(PE_CACHE_AUTO_THRESHOLD == threshold)
        threshold = (pe_cache_count > 1) ? pc_ex->cache_size : 0;

#ifdef LOG_PE_CACHE
    UINT32 delta = osal_get_tick() - pc->read_tick;
    if(delta)
        libc_printf("%s[%d]: size=0x%x, threshold=0x%x, data_len=0x%x, speed=%d KB/s\n", __FUNCTION__, cache_id, 
                    size, threshold, pe_cache_get_data_len(cache_id), pc->read_size/delta);
    pc->read_size = size;
    UINT32 ts = osal_get_tick();
#endif

    if(size > threshold)
        threshold = size;
#ifdef ENABLE_PE_FLAG
    if(threshold > pe_cache_get_data_len(cache_id) && !pe_cache_check_eof(cache_id))
    {
        UINT32 flgptn = 0;
        BOOL wait_data = TRUE;
        PE_CACHE_MUTEX_LOCK(pc->mutex, OSAL_WAIT_FOREVER_TIME);
        if(PE_FILE_FAILED == pc->sub_status)
        {
            PE_CACHE_MUTEX_UNLOCK(pc->mutex);
            return 0;
        }
        pc->req_size = size;
        PE_CACHE_MUTEX_UNLOCK(pc->mutex);
        osal_flag_wait(&flgptn, pc->flag, PE_FLAG_DATA_READY, OSAL_TWF_ANDW|OSAL_TWF_CLR, OSAL_WAIT_FOREVER_TIME);
    }
#else
    while((threshold > pe_cache_get_data_len(cache_id)) && !pe_cache_check_eof(cache_id))
        osal_task_sleep(1);
#endif

#ifdef LOG_PE_CACHE
    pc->read_delay += osal_get_tick() - ts;
    pc->read_count++;
#endif

    if(PE_CACHE_OPENED == pc_ex->status)
    {
        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
        if(pc_ex->sub_status != PE_FILE_FAILED && pc_ex->data_len)
        {
            if(pc_ex->rd_pos < pc_ex->wr_pos)
                bytes_returned = pc_ex->wr_pos - pc_ex->rd_pos;
            else
                bytes_returned = pc_ex->cache_size - pc_ex->rd_pos;

            if(bytes_returned > size)
                bytes_returned = size;
            *ptr = &pc_ex->cache_buff[pc_ex->rd_pos];
        }
        PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
    }

#ifdef LOG_PE_CACHE
    delta = osal_get_tick() - ts;
    if(delta)
        libc_printf("get_speed=%d KB/s, read_time=%d\n", bytes_returned/delta, delta);
    else
        libc_printf("get_speed=[MAX]\n");
    pc->read_tick = osal_get_tick();
#endif

    return bytes_returned;
}

void pe_cache_invalidate(int cache_id, UINT8 *ptr, UINT32 size)
{
    pe_cache_ex *pc_ex = &pe_cache_info_ex[cache_id];

    ASSERT(cache_id >= 0 && cache_id < PE_CACHE_NUM);
    if(PE_CACHE_OPENED == pc_ex->status)
    {
        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
        ASSERT(pc_ex->rd_pos == ((UINT32)ptr - (UINT32)pc_ex->cache_buff));
        ASSERT(size <= pc_ex->data_len);
        if(pc_ex->data_len)
        {
            ASSERT(((pc_ex->rd_pos >= pc_ex->wr_pos) && (size <= (pc_ex->cache_size - pc_ex->rd_pos))) || 
                   (size <= (pc_ex->wr_pos - pc_ex->rd_pos)));
            pc_ex->rd_pos += size;
            if(pc_ex->rd_pos >= pc_ex->cache_size)
                pc_ex->rd_pos = 0;
            pc_ex->data_len -= size;
        }
        PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
    }
}

int pe_cache_seek(int cache_id, off_t offset, int where)
{
    int ret = -1;
    p_pe_cache pc = (p_pe_cache)&pe_cache_info[cache_id];
	pe_cache_ex *pc_ex = &pe_cache_info_ex[cache_id];
    off_t cache_start_offset;
    off_t cache_end_offset;

    ASSERT((UINT32)cache_id < PE_CACHE_NUM);
    PE_CACHE_MUTEX_LOCK(pe_cache_mutex, OSAL_WAIT_FOREVER_TIME);
    if(PE_CACHE_OPENED == pc_ex->status && PE_FILE_FAILED != pc_ex->sub_status)
    {
	if(pc->file_seek != NULL)
	{
		if (pc->file_tell != NULL)
		{
			cache_start_offset = pc_ex->file_offset - (off_t)(pc_ex->data_len);
			cache_end_offset = pc_ex->file_offset;
			ret = 0;

#ifdef SUPPORT_ES_PLAYER
			if(g_libstream_enable == 1)
				pe_pre_buffer_size = 10240;
#endif
			
			switch (where)
			{
				case SEEK_SET:
				{
					if (offset < 0)
					{
						ret = -1;
					}
					
					break;
				}
				case SEEK_CUR:
				{
					offset += cache_start_offset;
					if ((offset < 0) || (offset > pc->file_size))
					{
						ret = -1;
					}
					
					break;
				}
				case SEEK_END:
				{	
					if (offset > 0)
					{
						ret = -1;
					}
					else
					{
						offset += pc->file_size;
					}
					
					break;
				}
				default:
				{
					ret = -1;
					break;
				}
			}

			if (ret != -1)
			{
				if ((offset < cache_start_offset))
				{
					//libc_printf("-");
					ret = pc->file_seek(pc->fp, offset, SEEK_SET);
					pc_ex->data_len = 0;
					pc_ex->rd_pos = 0;
					pc_ex->wr_pos = 0;					
				}
				else if (offset > cache_end_offset)
				{
					//libc_printf("+");
					ret = pc->file_seek(pc->fp, offset, SEEK_SET);
					pc_ex->data_len = 0;
					pc_ex->rd_pos = 0;
					pc_ex->wr_pos = 0;
				}
				else
				{
					pc_ex->data_len -= (offset - cache_start_offset);
					pc_ex->rd_pos += (offset - cache_start_offset);
					if (pc_ex->rd_pos >= pc_ex->cache_size)
					{
						pc_ex->rd_pos -= pc_ex->cache_size;
					}
				}
			}
			
		}
		else
		{
			if(SEEK_CUR == where)
			{
				offset -= (off_t)(pc_ex->data_len);
			}

			
		ret = pc->file_seek(pc->fp, offset, where);
		pc_ex->data_len = 0;
		pc_ex->rd_pos = 0;
		pc_ex->wr_pos = 0;

		}
		
	}
	else//not support seek function such as network
	{//handle seek function at  pe_cache
		switch(where)
		{
			case SEEK_SET:
				//partial support only can seek to pe_cache buffer start
				if(pc_ex->rd_pos < pc_ex->wr_pos) 
				{
					pc_ex->data_len += pc_ex->rd_pos;
					pc_ex->rd_pos = 0;
					ret=0;
				}
				else
				{
					ret = -1;
				}
				break;
			case SEEK_CUR:
				if((offset >=0) &&(offset <=pc_ex->data_len))
				{
					pc_ex->data_len -= offset;
					pc_ex->rd_pos += offset;
					if(pc_ex->rd_pos >= pc_ex->cache_size)
						pc_ex->rd_pos -= pc_ex->cache_size;	
					ret=0;
				}
				else if((offset <0) && ((-1*offset) <= PE_PREVIOUS_BUFFER_SIZE)) 
				{
					#if 0
					if((pc->rd_pos < pc->wr_pos) && (-1* offset <= pc->rd_pos))
					{
						pc->data_len+=(-1* offset);
						pc->rd_pos-=(-1* offset);
						ret = 0;
					}
					else
					{
						ret = -1;
					}
					#else
						pc_ex->data_len+=(-1* offset);
						if(pc_ex->rd_pos >= (-1* offset))
							pc_ex->rd_pos-=(-1* offset);
						else
							pc_ex->rd_pos = pc_ex->rd_pos+pc_ex->cache_size -(-1* offset);
						ret = 0;
					#endif
				}
				else
				{
					ret = -1;
				}
				break;
			case SEEK_END:
				ret = -1;
				//Not support seek to end.
				break;
		}
	}
	if(pc->file_eof != NULL)
		pc_ex->sub_status = pc->file_eof(pc->fp) ? PE_FILE_EOF : PE_FILE_READING;
	else
		pc_ex->sub_status = PE_FILE_READING;

	if(pc->file_tell != NULL)
	{
		pc_ex->file_offset = pc->file_tell(pc->fp);
	}
    }
    PE_CACHE_MUTEX_UNLOCK(pe_cache_mutex);
    return ret;
}

int pe_cache_check_eof(int cache_id)
{
    p_pe_cache pc = (p_pe_cache)&pe_cache_info[cache_id];
	pe_cache_ex *pc_ex = &pe_cache_info_ex[cache_id];

    if(cache_id <0 || cache_id >= PE_CACHE_NUM)
        return 0;
    if(PE_FILE_FAILED == pc_ex->sub_status)
        return 1;
	int result = 1;

    ASSERT((UINT32)cache_id < PE_CACHE_NUM);
    if(PE_CACHE_OPENED == pc_ex->status)
    {
        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
        if(PE_FILE_READING == pc_ex->sub_status)
            result = 0;
        PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
    }
    return result;
}

UINT32 pe_cache_get_data_len(int cache_id)
{
    pe_cache_ex *pc_ex = &pe_cache_info_ex[cache_id];
    UINT32 data_len = 0;

    ASSERT((UINT32)cache_id < PE_CACHE_NUM);
    if(PE_CACHE_OPENED == pc_ex->status)
    {
        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
        data_len = pc_ex->data_len;
        PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
    }
    return data_len;
}

off_t pe_cache_tell(int cache_id)
{
    p_pe_cache pc = (p_pe_cache)&pe_cache_info[cache_id];
	pe_cache_ex * pc_ex = &pe_cache_info_ex[cache_id];
    off_t offset = 0;
	if(pc->file_tell ==NULL)
	{
	  return offset;//don't return tell when play net work stream
	}
	else
	{
	    ASSERT((UINT32)cache_id < PE_CACHE_NUM);
	    if(PE_CACHE_OPENED == pc_ex->status)
	    {
	        PE_CACHE_MUTEX_LOCK(pc_ex->mutex, OSAL_WAIT_FOREVER_TIME);
	        offset = pc_ex->file_offset - (off_t)(pc_ex->data_len);			
	        PE_CACHE_MUTEX_UNLOCK(pc_ex->mutex);
	    }
	    return offset;
	}
}

